﻿using System;
using System.Collections.Generic;
using System.Text;
using ITAP.Core.Modelling.Primitives;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling;

namespace ITAP.ShprossesBuilder.Modelling.Components.Constraints
{
    [Serializable]
    /// <summary>
    /// Ограничение, которое "насаживает" концы примитива компонента на компоненты заданного типа. 
    /// Под этим понимается само визуальное "одевание", подсчёт углов среза, если, допустим, 
    /// профиль "насаживается" на коннектор, который расположен под углом к заглушке. При одевании считается, что 
    /// угол среза берётся у конца IEndedPrimitive.StartPoint.
    /// Применимо только к компонентам, реализующим IEndedComponent.
    /// </summary>
    public class ConnectedComponentsConnectorNozzleConstraint : IConstraint
    {
        private EndType endToNozzle;
        private ConstraintAction action;
        private bool enabled = true;
        private Type typeOfConnectedComponent;

        public ConnectedComponentsConnectorNozzleConstraint(Type typeOfConnectedComponent, EndType endToNozzle, ConstraintAction action)
        {
            this.endToNozzle = endToNozzle;
            this.action = action;
            this.typeOfConnectedComponent = typeOfConnectedComponent;
        }

        #region IConstraint Members

        public bool Check(IComponent component)
        {            
            int correctCuts = 0;
            int connectedComponents = 0;
            foreach (IComponent connectedComponent in component.ConnectedComponents)
            {
                if (!(connectedComponent is IEndedComponent)) continue;
                if (connectedComponent.GetType() != typeOfConnectedComponent) continue;

                connectedComponents++;

                IEndedComponent endedComponent = (IEndedComponent)component;
                IEndedComponent endedConnectedComponent = (IEndedComponent)connectedComponent;

                IWidePrimitive componentPrimitive = (IWidePrimitive)component.Primitives[0];
                IWidePrimitive connectedComponentPrimitive = (IWidePrimitive)connectedComponent.Primitives[0];

                Line cutLine = null;
                ModelPoint[] newPoints = null;
                if (endedConnectedComponent.StartConnectedComponent == endedComponent)
                {
                    switch (endToNozzle)
                    {
                        case EndType.Start:
                            cutLine = new Line(componentPrimitive.UpBorder.StartPoint,
                                componentPrimitive.DownBorder.StartPoint);

                            //newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.Start);
                            //}
                            //catch
                            //{
                            //    return false;
                            //}
                            if (newPoints == null)
                                return false;

                            if (connectedComponentPrimitive.DownBorder.StartPoint.Equals(newPoints[4]) &&
                                connectedComponentPrimitive.UpBorder.StartPoint.Equals(newPoints[8]))// && 
                               // connectedComponentPrimitive.StartPoint.Equals(newPoints[0]))
                                correctCuts++;
                            break;
                        case EndType.End:
                            cutLine = new Line(componentPrimitive.UpBorder.EndPoint,
                                componentPrimitive.DownBorder.EndPoint);

                            //newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.Start);
                            //}
                            //catch
                            //{
                            //    return false;
                            //}
                            if (newPoints == null)
                                return false;

                            if (connectedComponentPrimitive.DownBorder.StartPoint.Equals(newPoints[4]) &&
                                connectedComponentPrimitive.UpBorder.StartPoint.Equals(newPoints[8])) //&& 
                               // connectedComponentPrimitive.StartPoint.Equals(newPoints[0]))
                                correctCuts++;

                            break;
                        default:
                            throw new Exception();
                    }
                }
                else if (endedConnectedComponent.EndConnectedComponent == endedComponent)
                {
                    switch (endToNozzle)
                    {
                        case EndType.Start:
                            cutLine = new Line(componentPrimitive.UpBorder.StartPoint,
                                componentPrimitive.DownBorder.StartPoint);

                            newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.End);
                            //}
                            //catch
                            //{
                            //    return false;
                            //}
                            if (newPoints == null)
                                return false;

                            if (connectedComponentPrimitive.DownBorder.EndPoint.Equals(newPoints[4]) &&
                                connectedComponentPrimitive.UpBorder.EndPoint.Equals(newPoints[8]))// && 
                                //connectedComponentPrimitive.EndPoint.Equals(newPoints[0]))
                                correctCuts++;
                            break;
                        case EndType.End:
                            cutLine = new Line(componentPrimitive.UpBorder.EndPoint,
                                componentPrimitive.DownBorder.EndPoint);

                            newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.End);
                            //}
                            //catch
                            //{
                            //    return false;
                            //}
                            if (newPoints == null)
                                return false;

                            if (connectedComponentPrimitive.DownBorder.EndPoint.Equals(newPoints[4]) &&
                                connectedComponentPrimitive.UpBorder.EndPoint.Equals(newPoints[8])) //&& 
                                //connectedComponentPrimitive.EndPoint.Equals(newPoints[0]))
                                correctCuts++;

                            break;
                        default:
                            throw new Exception();
                    }

                }
            }

            if (correctCuts < connectedComponents)
                return false;

            return true;
        }

        public void Fix(IComponent component)
        {
            if (!enabled) return;

            foreach (IComponent connectedComponent in component.ConnectedComponents)
            {
                if (!(connectedComponent is IEndedComponent)) continue;
                if (connectedComponent.GetType() != typeOfConnectedComponent) continue;


                IEndedComponent endedComponent = (IEndedComponent)component;
                IEndedComponent endedConnectedComponent = (IEndedComponent)connectedComponent;

                IWidePrimitive componentPrimitive = (IWidePrimitive)component.Primitives[0];
                IWidePrimitive connectedComponentPrimitive = (IWidePrimitive)connectedComponent.Primitives[0];

                Line cutLine = null;
                ModelPoint[] newPoints = null;
                if (endedConnectedComponent.StartConnectedComponent == endedComponent)
                {
                    switch (endToNozzle)
                    {
                        case EndType.Start:
                            cutLine = new Line(componentPrimitive.UpBorder.StartPoint,
                                componentPrimitive.DownBorder.StartPoint);

                            newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.Start);
                            //}
                            //catch
                            //{
                                
                            //    continue;
                            //}
                            if (newPoints == null)
                                continue;

                          //  connectedComponentPrimitive.StartPoint = (ModelPoint)newPoints[0].Clone();

                            connectedComponentPrimitive.DownBorder.StartPoint = (ModelPoint)newPoints[4].Clone();
                            connectedComponentPrimitive.Update();
                            connectedComponentPrimitive.UpBorder.StartPoint = (ModelPoint)newPoints[8].Clone();
                            connectedComponentPrimitive.Update();
                            break;
                        case EndType.End:
                            cutLine = new Line(componentPrimitive.UpBorder.EndPoint,
                                componentPrimitive.DownBorder.EndPoint);

                            newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.Start);
                            //}
                            //catch
                            //{
                            //    continue;
                            //}
                            if (newPoints == null)
                                continue;

                           // connectedComponentPrimitive.StartPoint = (ModelPoint)newPoints[0].Clone();

                            connectedComponentPrimitive.DownBorder.StartPoint = (ModelPoint)newPoints[4].Clone();
                            connectedComponentPrimitive.Update();
                            connectedComponentPrimitive.UpBorder.StartPoint = (ModelPoint)newPoints[8].Clone();
                            connectedComponentPrimitive.Update();
                            break;
                        default:
                            throw new Exception();
                    }
                }
                else if (endedConnectedComponent.EndConnectedComponent == endedComponent)
                {                    
                    switch (endToNozzle)
                    {
                        case EndType.Start:
                            cutLine = new Line(componentPrimitive.UpBorder.StartPoint,
                                componentPrimitive.DownBorder.StartPoint);

                            newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.End);
                            //}
                            //catch
                            //{
                            //    continue;
                            //}
                            if (newPoints == null)
                                continue;

                          //  connectedComponentPrimitive.EndPoint = (ModelPoint)newPoints[0].Clone();

                            connectedComponentPrimitive.DownBorder.EndPoint = (ModelPoint)newPoints[4].Clone();
                            connectedComponentPrimitive.Update();
                            connectedComponentPrimitive.UpBorder.EndPoint = (ModelPoint)newPoints[8].Clone();
                            connectedComponentPrimitive.Update();
                            break;
                        case EndType.End:
                            cutLine = new Line(componentPrimitive.UpBorder.EndPoint,
                                componentPrimitive.DownBorder.EndPoint);

                            newPoints = null;
                            //try
                            //{
                                newPoints = connectedComponentPrimitive.Intersect(cutLine, EndType.End);
                            //}
                            //catch
                            //{
                            //    continue;
                            //}
                            if (newPoints == null)
                                continue;

                            //connectedComponentPrimitive.EndPoint = (ModelPoint)newPoints[0].Clone();

                            connectedComponentPrimitive.DownBorder.EndPoint = (ModelPoint)newPoints[4].Clone();
                            connectedComponentPrimitive.Update();
                            connectedComponentPrimitive.UpBorder.EndPoint = (ModelPoint)newPoints[8].Clone();
                            connectedComponentPrimitive.Update();

                            break;
                        default:
                            throw new Exception();
                    }
                }
            }
        }

        public bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                enabled = value;
            }
        }

        #endregion

        #region IConstraint Members


        public ConstraintAction Action
        {
            get { return action; }
        }

        #endregion


        #region ICloneable Members

        public object Clone()
        {
            ConnectedComponentsConnectorNozzleConstraint clone = new ConnectedComponentsConnectorNozzleConstraint(typeOfConnectedComponent, endToNozzle, action);
            return clone;
        }

        #endregion
    }
}
